﻿/*
 * BValue
 * 
 * Author: snowdreamist
 * Date: 2011/11/19
 * 
 */


using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BitTorrentSharp.BEncoding
{
    public abstract class BValue
    {
        #region static
        /// <summary>
        /// Deserialize
        /// </summary>
        public static BValue Deserialize(Stream inputStream)
        {
            if (inputStream == null)
                throw new ArgumentNullException("inputStream");
            // Encoding used to PeekChar(), ASCII maps to 7bits -> 1byte char
            // PeekByte() will be more appropriate if it exists
            return Deserialize(new BinaryReader(inputStream, Encoding.ASCII));
        }
        /// <summary>
        /// Deserialize
        /// </summary>
        internal static BValue Deserialize(BinaryReader reader)
        {
            var topChar = (char)reader.PeekChar();

            switch (topChar)
            {
                case 'l':
                    {
                        var value = new BList();
                        value.InternalDeserialize(reader);
                        return value;
                    }

                case 'd':
                    {
                        var value = new BDictionary();
                        value.InternalDeserialize(reader);
                        return value;
                    }

                case 'i':
                    {
                        var value = new BNumber();
                        value.InternalDeserialize(reader);
                        return value;
                    }

                case '0':
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9':
                    {
                        var value = new BString();
                        value.InternalDeserialize(reader);
                        return value;
                    }

                default:
                    throw new BEncodingException(string.Format("Unknown type, input {0}", topChar));
            }
        }
        /// <summary>
        /// get string's orginal bytes
        /// </summary>
        /// <param name="source">source string</param>
        /// <returns></returns>
        internal static byte[] GetOrginalBytes(string source)
        {
            if (source == null)
                throw new ArgumentNullException("source");
            return Encoding.Default.GetBytes(source);
        }

        #endregion

        public BValue()
        {
            this.IsReadOnly = false;
        }
        /// <summary>
        /// Is read only or not
        /// </summary>
        public bool IsReadOnly { get; internal set; }
        /// <summary>
        /// Serialize to stream
        /// </summary>
        /// <returns></returns>
        public void Serialize(Stream outputStream)
        {
            if (outputStream == null)
                throw new ArgumentNullException("outputStream");
            // See Deserialize
            using (BinaryWriter writer = new BinaryWriter(outputStream, Encoding.ASCII))
                this.Serialize(writer);
        }
        /// <summary>
        /// Only serialize content
        /// </summary>
        /// <param name="outputStream"></param>
        public void SerializeContent(Stream outputStream)
        {
            if (outputStream == null)
                throw new ArgumentNullException("outputStream");
            // See Deserialize
            using (BinaryWriter writer = new BinaryWriter(outputStream, Encoding.ASCII))
                this.SerializeContent(writer);
        }
        /// <summary>
        /// Serialize
        /// </summary>
        internal void Serialize(BinaryWriter writer)
        {
            if (writer == null)
                throw new ArgumentNullException("writer");
            this.InternalSerialize(writer);
        }
        /// <summary>
        /// Only serialize content
        /// </summary>
        /// <param name="outputStream"></param>
        internal void SerializeContent(BinaryWriter writer)
        {
            this.InternalSerializeContent(writer);
        }
        /// <summary>
        /// Deserialize from binary reader
        /// </summary>
        /// <param name="inputStream"></param>
        /// <returns></returns>
        internal protected abstract void InternalDeserialize(BinaryReader reader);
        /// <summary>
        /// Serialize to binary writer
        /// </summary>
        /// <param name="outputStream"></param>
        /// <returns></returns>
        internal protected abstract void InternalSerialize(BinaryWriter writer);
        /// <summary>
        /// OnlySerializeContent
        /// </summary>
        /// <param name="writer"></param>
        internal protected abstract void InternalSerializeContent(BinaryWriter writer);
    }
}
